home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 October / Macworld (1998-10).dmg / Shareware World / Info / For Developers / MacZoop 1.8.4 / More Classes / File Classes / ZJPEGFile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-01  |  17.9 KB  |  732 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZJPEGFile.cpp        -- a file object that can open JPEG images
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21. #include    "ZJPEGFile.h"
  22. #include    "ZGWorld.h"
  23. #include    "MacZoop.h"
  24.  
  25. #include    <ImageCompression.h>
  26. #include    <FixMath.h>
  27.  
  28. static             OSErr    ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld );
  29.  
  30.  
  31. /*-------------------------------------  CONSTRUCTOR ------------------------------------------*/
  32.  
  33.  
  34. ZJPEGFile::ZJPEGFile( const FSSpec& aSpec )
  35.     : ZFile( aSpec )
  36. {
  37.     FailOSErr( gMacInfo.hasImgCompressionMgr? noErr : kNoSystemSupportForFeatureErr );
  38. }
  39.  
  40.  
  41. /***********************************************************************************************
  42. *
  43. *    READ- read the JPEG file into ZGWorld object (recommended approach)
  44. *
  45. ***********************************************************************************************/
  46.  
  47. void    ZJPEGFile::Read( ZGWorld*   aGWorld )
  48. {
  49.     FailNILParam( aGWorld );
  50.     
  51.     if ( refNum == _NOT_OPEN )
  52.         FailOSErr( fnOpnErr );
  53.     
  54.     GWorldPtr    gw = aGWorld->GetMacGWorld();
  55.     
  56.     if ( gw )
  57.         DisposeGWorld( gw );
  58.         
  59.     aGWorld->SetMacGWorld( gw = NULL );
  60.     
  61.     Read( &gw );
  62.     
  63.     // now we need to make the ZGWorld object handle this GWorld that was built
  64.     // by the image compression manager. WARNING! If an exception was thrown reading the file
  65.     // the GWorld, the original GWorld will have been discarded and trying to access it via
  66.     // the object will probably crash. However, normally this is called to create a new image
  67.     // from a file, so this shouldn't be a problem. Bear it in mind for other uses however.
  68.     
  69.     aGWorld->SetMacGWorld( gw );
  70. }
  71.  
  72.  
  73. /***********************************************************************************************
  74. *
  75. *    READ- read the JPEG file into a Macintosh GWorld
  76. *
  77. ***********************************************************************************************/
  78.  
  79. void    ZJPEGFile::Read( GWorldPtr* aGWorld )
  80. {
  81.     FailNILParam( aGWorld );
  82.     FailOSErr( ReadJFIFToGWorld( refNum, aGWorld ));
  83. }
  84.  
  85.  
  86. #pragma mark -
  87. #pragma mark ####  static JPEG Decoding stuff ####
  88.  
  89. // prototypes:
  90.  
  91. static pascal     OSErr    JPEGDataLoader( Ptr *dataP, long bytesNeeded, long refCon );
  92. static ImageDescriptionHandle    ScanJPEG( short originalFile, Ptr *data, OpenCPicParams *pictureHeader );
  93. static void  SwallowQuantTable( char *data );
  94. static void  SwallowHuffTable( char *data );
  95. static char *MarkerDetect( char *data, short *width, short *height, long *hRes, long *vRes, short *depth );
  96.  
  97. // switches, etc:
  98.  
  99. //#define    USE_DATA_LOADER
  100. #define    kDefaultBufferSize    (64L * 1024L)    // 64K buffer size
  101.  
  102. /*
  103.     Written by:    Mark Krueger, Apple Computer, Inc.
  104.     
  105.     The JPEG File  Interchange Format ( JFIF) is a cross platform standard file format for
  106.     storing JPEG compressed image files. This application shows you how you can easily convert
  107.     these to QuickTime PICT format, or vice-verse. 
  108.     
  109.     You can use this in your application to open JFIF files directly or put a user interface 
  110.     ( Drag and Drop would be cool ) on it to make a conversion program ). When files are to 
  111.     be used on the Mac it is best to keep them in QTPict format so they can be easily copied
  112.     and pasted, but JFIF format is useful for transfering data to other platforms that cannot
  113.     use PICT formated JPEG images. 
  114.     
  115.     NOTE: the PICT to JFIF format translator is incomplete in that it only converts PICT
  116.     files which are already in QuickTIme JPEG format and it does not handle banded JPEG
  117.     Picts ( which may be commonly created by QuickTime applications which call 
  118.     CompressPictureFile in low memory conditions or which create their own banded picts)
  119.     To fully handle these images, the individual bands would have to be converted into a 
  120.     single JPEG stream when put into JFIF format, and this code does not show you how
  121.     to do that. 
  122.  
  123. */
  124.  
  125. // static functions, mostly courtesy Apple Computer DTS:
  126.  
  127.  
  128.  
  129. static OSErr    ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld )
  130. {
  131.     // simpler and more robust function for reading JFIF images. Because this does not go through the step of
  132.     // decompressing the image to a picture, it requires less memory than the above function. It resizes the
  133.     // GWorld to the size of the image. This may alter the GWorld ptr value, so be sure to copy it back to any
  134.     // data structures that have reference to it- e.g. layers. 
  135.  
  136.     OSErr                    result = noErr;
  137.     OpenCPicParams            header;
  138.     Ptr                        data = NULL;
  139.     Handle                    tempBuffer = NULL;
  140.     ImageDescriptionHandle    desc = NULL;
  141.     GWorldFlags                flags;
  142.     PixMapHandle            ppix;
  143.     CGrafPtr                savePort;
  144.     GDHandle                saveDevice;
  145.     ICMProgressProcRecord    iProgRec = {NULL,0};
  146.     ICMDataProcRecord        dLoadProc = {NULL,0};
  147.     
  148.     #ifdef USE_DATA_LOADER
  149.         if ((desc = ScanJPEG(refNum,NULL,&header)) == NULL)
  150.         {
  151.             result = paramErr;
  152.             goto done;
  153.         }
  154.         (*desc)->dataSize = 0;
  155.     #else
  156.         if ((desc = ScanJPEG(refNum,&data,&header)) == NULL)
  157.         {
  158.             result = paramErr;
  159.             goto done;
  160.         }
  161.     #endif
  162.     // we have a description of the image. Now resize the GWorld and set its depth. Note that we do not
  163.     // call UpdateGWorld, but simply get rid of the existing one and reallocate it. This requires half the
  164.     // memory it otherwise would because no copy is required. This is ok because we are not attempting to keep
  165.     // the existing image anyway.
  166.     
  167.     if (*theGWorld)
  168.         DisposeGWorld(*theGWorld);
  169.     
  170.     result = NewImageGWorld(theGWorld,desc,0);
  171.     
  172.     if (result)
  173.         goto done;
  174.         
  175.     // we succeeded in changing the GWorld to the size we desire, now we simply decompress the data into
  176.     // the GWorld's pixmap. We load the JPEG data using a small buffer to save memory, so set this up here
  177.     
  178.     #ifdef USE_DATA_LOADER
  179.         dLoadProc.dataProc = NewICMDataProc((ProcPtr) JPEGDataLoader);
  180.         dLoadProc.dataRefCon = refNum;
  181.         result = SetFPos(refNum,fsFromStart,0);
  182.  
  183.         tempBuffer = NewHandleClear(kDefaultBufferSize);
  184.         if (tempBuffer == NULL)
  185.         {
  186.             result = memFullErr;
  187.             goto done;
  188.         }
  189.         else
  190.         {
  191.             MoveHHi(tempBuffer);
  192.             HLock(tempBuffer);
  193.         
  194.             data = StripAddress(*tempBuffer);
  195.         }
  196.     #endif
  197.  
  198.     //iProgRec.progressProc = NewICMProgressProc((ProcPtr) StdDecompressionProgressProc);
  199.     //iProgRec.progressRefCon = 'open';    
  200.     
  201.     if (LockPixels(ppix = GetGWorldPixMap(*theGWorld)))
  202.     {
  203.         GetGWorld(&savePort,&saveDevice);    
  204.         SetGWorld(*theGWorld,NULL);
  205.         
  206.     #ifdef USE_DATA_LOADER
  207.         result = FDecompressImage(  data,desc,ppix,
  208.                                     NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
  209.                                     NULL,(Rect *) NULL,codecHighQuality,anyCodec,kDefaultBufferSize,
  210.                                     (ICMDataProcRecordPtr) &dLoadProc,
  211.                                     (ICMProgressProcRecordPtr) &iProgRec);
  212.     #else
  213.         result = FDecompressImage(  data,desc,ppix,
  214.                                     NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
  215.                                     NULL,(Rect *) NULL,codecHighQuality,anyCodec,0,
  216.                                     (ICMDataProcRecordPtr) NULL,
  217.                                     (ICMProgressProcRecordPtr) &iProgRec);
  218.     #endif
  219.         SetGWorld(savePort,saveDevice);
  220.         UnlockPixels(ppix);
  221.     }
  222.     else
  223.         result = -50;
  224. done:
  225.     if (tempBuffer)
  226.     {
  227.         HUnlock(tempBuffer);
  228.         DisposeHandle(tempBuffer);
  229.     }
  230.     if (desc) 
  231.         DisposeHandle((Handle) desc);
  232.     if (iProgRec.progressProc)
  233.         DisposeRoutineDescriptor(iProgRec.progressProc);
  234.     if (dLoadProc.dataProc)
  235.         DisposeRoutineDescriptor(dLoadProc.dataProc);
  236.     return result;
  237. }
  238.  
  239.  
  240.  
  241. static pascal OSErr    JPEGDataLoader(Ptr *dataP,long bytesNeeded,long refCon)
  242. {
  243.     OSErr    theErr = noErr;
  244.     short    fileRefNum = LoWord(refCon);
  245.     
  246.     if (dataP)
  247.         theErr = FSRead(fileRefNum,&bytesNeeded,*dataP);
  248.     else
  249.         theErr = SetFPos(fileRefNum,fsFromMark,bytesNeeded);
  250.     return theErr;
  251. }
  252.  
  253.  
  254. /************************************************
  255.  
  256.     Scan a file for valid JPEG data, and fill in a picture header and ImageDescription
  257.     for it.
  258.  
  259. *************************************************/
  260.  
  261. static ImageDescriptionHandle    ScanJPEG(short originalFile,Ptr *data,OpenCPicParams *pictureHeader)
  262. {
  263.     short                     w,h;
  264.     ImageDescriptionHandle     desc;
  265.     long                    l;
  266.     char                    *bitStream,*scanData,*buffer;
  267.     long                    hRes = 72L<<16,vRes = 72L<<16;
  268.     short                    depth = 32;
  269.     
  270.     // this creates a buffer which holds the whole image. This is not very efficient. Instead we will spool
  271.     // the data in using a data loading procedure.
  272.     
  273.     
  274.     GetEOF(originalFile,&l);
  275.     if ((buffer = NewPtr(l)) == nil)
  276.         return(nil);
  277.  
  278.     FSRead(originalFile,&l,buffer);
  279.     bitStream = buffer;
  280.     
  281.     if ( (desc = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription))) == nil )
  282.         return(nil);
  283.  
  284.     if ( (scanData = MarkerDetect(bitStream,&w,&h,&hRes,&vRes,&depth)) == 0 )
  285.         return(nil);
  286.  
  287.     (*desc)->idSize = sizeof(ImageDescription);
  288.     (*desc)->width = w;
  289.     (*desc)->height = h;
  290.     (*desc)->temporalQuality = 0;
  291.     (*desc)->spatialQuality = codecNormalQuality;
  292.     (*desc)->dataSize = l;
  293.     (*desc)->cType = 'jpeg';
  294.     (*desc)->version = 0;
  295.     (*desc)->revisionLevel = 0;
  296.     (*desc)->vendor = 0;
  297.     (*desc)->hRes = hRes;
  298.     (*desc)->vRes = vRes;
  299.     (*desc)->depth = depth;
  300.     (*desc)->clutID = -1;
  301.     BlockMoveData("\pPhoto",(*desc)->name,6);
  302.     SetRect(&pictureHeader->srcRect,0,0,w,h);
  303.     pictureHeader->version = -2;
  304.     pictureHeader->reserved1 = 0;
  305.     pictureHeader->reserved2 = 0;
  306.     pictureHeader->hRes = hRes;
  307.     pictureHeader->vRes = vRes;
  308.     
  309.     if (data == NULL)
  310.     {
  311.         // get rid of this buffer- we are going to load the image in small pieces
  312.     
  313.         DisposePtr(buffer);
  314.     }
  315.     else
  316.         *data = buffer;
  317.         
  318.     return(desc);
  319. }
  320.  
  321.  
  322.  
  323. /**********************************************************************
  324.  
  325.     JPEG specific stuff.
  326.     
  327. ***********************************************************************/
  328.  
  329. /*
  330.  
  331.     JPEG Marker code definitions.
  332.     
  333. */
  334.  
  335. #define    MARKER_PREFIX    0xff
  336. #define    MARKER_SOI    0xd8        /* start of image */
  337. #define    MARKER_SOF    0xc0        /* start of frame */
  338. #define    MARKER_DHT    0xc4        /* define Huffman table */
  339. #define    MARKER_EOI    0xd9        /* end of image */
  340. #define    MARKER_SOS    0xda        /* start of scan */
  341. #define    MARKER_DQT    0xdb        /* define quantization tables */
  342. #define    MARKER_DNL    0xdc        /* define quantization tables */
  343. #define    MARKER_DRI    0xdd        /* define Huffman table */
  344. #define    MARKER_COM    0xfe        /* comment */
  345. #define MARKER_APP0    0xe0        
  346.  
  347.  
  348. /**********************************************************************
  349.  
  350.     Read the quantization table from the JPEG bitstream.
  351.     
  352. ***********************************************************************/
  353.  
  354. static void  SwallowQuantTable(char *data)
  355. {
  356.     long    i;
  357.     long    length,pm,nm;
  358.  
  359.     length = *(short *)data;            /* read length */
  360.     length -= 2;
  361.     data += 2;
  362.     while ( length ) {
  363.         nm= *data++;                    /* read precision and number */
  364.         pm = nm>>4;    
  365.         nm &= 0xf;
  366.         length--;
  367.         if ( pm ) {
  368.             for(i=0;i<64;i++) {
  369.                 length -= 2;
  370.                 data += 2;
  371.             }
  372.         } else {
  373.             for(i=0;i<64;i++) {
  374.                 length--;
  375.                 data++;
  376.             }
  377.         }
  378.     }    
  379. }
  380.  
  381. /**********************************************************************
  382.  
  383.     Read the huffman table from the JPEG bitstream.
  384.     
  385. ***********************************************************************/
  386.  
  387. static void  SwallowHuffTable(char *data)
  388. {
  389.     short    i,tc,id;
  390.     long    length;
  391.     
  392.     unsigned char    bin[17];
  393.     unsigned char    val[256];
  394.  
  395.     bin[0] = 0;
  396.     length = *(short *)data;            /* read length */
  397.     data += 2;
  398.     length -= 2;
  399.     while ( length ) {
  400.         id=*data++;                /* read id */
  401.         length--;
  402.         if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
  403.             return;
  404.         }
  405.         tc = 0;
  406.         for(i=0;i<16;i++) {
  407.             length--;
  408.             tc += (bin[i+1] = *data++);
  409.         }
  410.         for (i=0; i < tc; i++ ) {
  411.             length--;
  412.             val[i] = *data++;
  413.         }
  414.     }
  415. }
  416.     
  417.     
  418.     
  419. /**********************************************************************
  420.  
  421.     Scan the JPEG stream for the proper markers and fill in the image parameters
  422.     
  423.     returns nil if it cant comprehend the data, otherwise a pointer to the start
  424.     of the JPEG data.
  425.     
  426.     
  427.     It does a cursory check on the JPEG data to see if it's reasonable.
  428.     Check out the ISO JPEG spec if you really want to know what's going on here.
  429.     
  430. ***********************************************************************/
  431.  
  432. static char *MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
  433. {
  434.     short    frame_field_length;
  435.     short    data_precision;
  436.     short    scan_field_length;
  437.     short    number_component,scan_components;
  438.     short    c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
  439.     short    dac_t1, dac_t2, dac_t3;
  440.     unsigned char    c;
  441.     short    qtabledefn;
  442.     short    htabledefn;
  443.     short    status;
  444.     short    length;
  445.     short    i;
  446.     
  447.     c = *data++;
  448.     qtabledefn = 0;
  449.     htabledefn = 0;
  450.     status = 0;
  451.     while (c != (unsigned char)MARKER_SOS) {
  452.         while (c != (unsigned char)MARKER_PREFIX)
  453.             c = *data++;                        /* looking for marker prefix bytes */
  454.         while (c == (unsigned char)MARKER_PREFIX)
  455.             c = *data++;                        /* (multiple?) marker prefix bytes */
  456.         if (c == 0)
  457.             continue;                                    /* 0 is never a marker code */
  458.  
  459.         if (c == (unsigned char)MARKER_SOF) {
  460.  
  461.             frame_field_length = *(short *)data;
  462.             data += 2;
  463.             data_precision = *data++;
  464.             
  465.             if ( data_precision != 8 ) { 
  466.                 status = 2;
  467.             }
  468.  
  469.             *height = *(short *)data;
  470.             data += 2;
  471.             *width = *(short *)data;
  472.             data += 2;
  473.                         
  474.             number_component = *data++;
  475.             
  476.             switch ( number_component  ) {
  477.             case 3:
  478.                 c1 = *data++;
  479.                 hv1 = *data++;
  480.                 q1 = *data++;
  481.                 c2 = *data++;
  482.                 hv2 = *data++;
  483.                 q2 = *data++;
  484.                 c3 = *data++;
  485.                 hv3 = *data++;
  486.                 q3 = *data++;
  487.                 *depth = 32;
  488.                 break;
  489.             case 1:        
  490.                 c1 = *data++;
  491.                 hv1 = *data++;
  492.                 q1 = *data++;
  493.                 *depth = 40;
  494.                 break;
  495.             default:
  496.                 status = 3;
  497.                 break;
  498.             }
  499.             continue;
  500.         }
  501.     
  502.         if (c == (unsigned char)MARKER_SOS) {
  503.             short tn;
  504.             scan_field_length = *(short *)data;
  505.             data += 2;
  506.             scan_components = *data++;
  507.             for ( i=0; i < scan_components; i++ ) {
  508.                 unsigned char cn,dac_t;
  509.                 
  510.                 cn = *data++;
  511.                 dac_t = *data++;
  512.                 if ( cn == c1 ) {
  513.                     dac_t1 = dac_t;
  514.                 } else if ( cn == c2 ) {
  515.                     dac_t2 = dac_t;
  516.                 } else if ( cn == c3 ) {
  517.                     dac_t3 = dac_t;
  518.                 } else {    
  519.                     status = 29;
  520.                     break;
  521.                 }
  522.             }
  523.             switch ( tn=(dac_t1 & 0xf) )  {
  524.             case 0:
  525.             case 1:
  526.                 break;
  527.             case 0xf:
  528.                 break;
  529.             default:
  530.                 status = 33;
  531.                 break;
  532.             }
  533.             switch (  tn=(dac_t2 & 0xf) )  {
  534.             case 0:
  535.             case 1:
  536.                 break;
  537.             case 0xf:
  538.                 break;
  539.             default:
  540.                 status = 33;
  541.                 break;
  542.             }
  543.             switch (  tn=(dac_t3 & 0xf) )  {
  544.             case 0:
  545.             case 1:
  546.                 break;
  547.             case 0xf:
  548.                 break;
  549.             default:
  550.                 status = 33;
  551.                 break;
  552.             }
  553.  
  554.  
  555.             /*  Initialize the DC tables */
  556.             
  557.             switch (  tn=dac_t1 & 0xf0 )  {
  558.             case 0:
  559.             case 0x10:
  560.                 break;
  561.             case 0xf0:
  562.                 break;
  563.             default:
  564.                 status = 34;
  565.                 break;
  566.             }
  567.             switch (  tn=dac_t2 & 0xf0 )  {
  568.             case 0:
  569.             case 0x10:
  570.                 break;
  571.             case 0xf0:
  572.                 break;
  573.             default:
  574.                 status = 34;
  575.                 break;
  576.             }
  577.             switch (  tn=dac_t3 & 0xf0 )  {
  578.             case 0:
  579.             case 0x10:
  580.                 break;
  581.             case 0xf0:
  582.                 break;
  583.             default:
  584.                 status = 34;
  585.                 break;
  586.             }
  587.             if ( *data++ != 0 )  {
  588. //                status = 18;
  589.             }
  590.             if ( *data++ != 63 )  {
  591. //                status = 19;
  592.             }
  593.             if ( *data++ != 0 ) {
  594. //                status = 20;
  595.             }
  596.             if ( status )
  597.                 return(0);
  598.             else
  599.                 return(data);
  600.         }
  601.  
  602.         if (c == (unsigned char)MARKER_DQT) {
  603.             scan_field_length = *(short *)data;
  604.             SwallowQuantTable(data);
  605.             data += scan_field_length;
  606.             continue;
  607.         }
  608.         if (c == (unsigned char)MARKER_DHT) {
  609.             scan_field_length = *(short *)data;
  610.             SwallowHuffTable(data);
  611.             continue;
  612.         }
  613.         if (c == (unsigned char)MARKER_DRI) {
  614.             length = *(short *)data;            /* read length */
  615.             data += 2;
  616.             length = *(short *)data;            
  617.             data += 2;
  618.             continue;
  619.         }
  620.         if (c == (unsigned char)MARKER_DNL) {
  621.             length = *(short *)data;            /* read length */
  622.             data += 2;
  623.             length = *(short *)data;            
  624.             data += 2;
  625.             continue;
  626.         }
  627.         if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
  628.             continue;
  629.         }
  630.  
  631.         if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI)    /* image start, end marker */
  632.             continue;
  633.  
  634.         if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
  635.             status = 12;
  636.             length = *(short *)data;            /* read length */
  637.             data += length;
  638.             continue;
  639.         }
  640.         if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
  641.             length = *(short *)data;            /* read length */
  642.             data += 2;
  643.             length -= 2;
  644.             if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
  645.                 char buf[5];
  646.                 buf[0] = *data++;
  647.                 buf[1] = *data++;
  648.                 buf[2] = *data++;
  649.                 buf[3] = *data++;
  650.                 buf[4] = *data++;
  651.                 length -= 5;
  652.                 
  653.                 if ( buf[0] == 'J' && buf[1] == 'F'  && buf[2] == 'I'  && buf[3] == 'F' ) {
  654.                     short    units;
  655.                     long    xres,yres;
  656.                     short    version;
  657.                     
  658.                     
  659.                     version = *(short *)data; data += 2;length -= 2;
  660.                     if ( version != 0x100 && version != 0x101 && version != 0x102) {
  661.                         status = 44;        // unknown JFIF version
  662.                         break;
  663.                     }
  664.                     units = *data++; length--;
  665.                     xres = *(short *)data; data += 2; length -= 2;
  666.                     yres = *(short *)data; data += 2; length -= 2;
  667.  
  668.                     switch ( units ) {
  669.                     case 0:            // no res, just aspect ratio
  670.                         // some files will have bad res data here. In this case, xRes & yRes will
  671.                         // be zero. This really means that they should be 1! This allows some dodgy
  672.                         // JPEGs created by some crappy PC software to be opened
  673.                         
  674.                         if (xres == 0)
  675.                             xres = 1;
  676.                         if (yres == 0)
  677.                             yres = 1;
  678.                     
  679.                         *hRes = FixMul(72L<<16,xres<<16);
  680.                         *vRes = FixMul(72L<<16,yres<<16);
  681.                         break;
  682.                     case 1:            // dots per inch
  683.                         *hRes = xres<<16;
  684.                         *vRes = yres<<16;
  685.                         break;
  686.                     case 2:            // dots per centimeter (we convert to dpi )
  687.                         *hRes = FixMul(0x28a3d,xres<<16);
  688.                         *vRes = FixMul(0x28a3d,xres<<16);
  689.                         break;    
  690.                     default:
  691.                         break;
  692.                     }
  693.                     xres = *data++; length--;
  694.                     yres = *data++; length--;
  695.                     
  696.                     /* skip JFIF thumbnail */
  697.                     
  698.                     xres *= yres;
  699.                     data += xres*3; length -= xres*3;
  700.                     
  701.                     if (  length != 0 ) {
  702.                         status = 44;        // bad jfif marker
  703.                         break;
  704.                     }
  705.                 }
  706.             }
  707.             data += length;
  708.             continue;
  709.         }
  710.         if (c == (unsigned char)MARKER_COM) {
  711.             length = *(short *)data;            /* read length */
  712.             data += length;
  713.             continue;
  714.         }
  715.         if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
  716.             length = *(short *)data;            /* read length */
  717.             data += length;
  718.             continue;
  719.         }
  720.         if ( c == 0x1 )
  721.             continue;
  722.         if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
  723.             length = *(short *)data;            /* read length */
  724.             status = 13;
  725.             data += length;
  726.             continue;
  727.         }
  728.     }
  729.     return(0);
  730. }
  731.  
  732.